﻿using System;
using System.Drawing;
using System.IO;
using System.IO.Ports;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using static System.Net.Mime.MediaTypeNames;

namespace SocketDemoClient
{
    public partial class MainForm : Form
    {
        public Socket ClientSocket { get; set; }

        public MainForm()
        {
            InitializeComponent();
        }

        private void btn_Connect_Click(object sender, EventArgs e)
        {
            // 客户端连接服务器端
            // 先创建Socket对象
            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            ClientSocket = socket;

            // 连接服务器端
            try
            {
                socket.Connect(IPAddress.Parse(txt_IP.Text), int.Parse(txt_Port.Text));
            }
            catch (Exception ex)
            {
                MessageBox.Show("连接失败，请重试");
                //Thread.Sleep(800);
                //btn_Connect_Click(this, e); // 这样操作，如果一直连接失败，可能会递归很多层，导致递归溢出
                return;
            }

            // 发送、接受消息
            Thread thread = new Thread(new ParameterizedThreadStart(ReceiveData));
            thread.IsBackground = true;
            thread.Start(ClientSocket);
        }

        #region 客户端接收数据
        public void ReceiveData(object socket)
        {
            var proxSocket = socket as Socket;
            byte[] data = new byte[1024 * 1024];
            while (true)
            {
                // 不断接受客户端传来的消息，并更新到聊天框上面
                int len = 0;
                try
                {
                    len = proxSocket.Receive(data, 0, data.Length, SocketFlags.None);
                }
                catch (Exception ex)
                {
                    // 异常退出
                    AppendTextToTxtLog(string.Format("服务器端{0}异常退出", proxSocket.RemoteEndPoint.ToString()));
                    // 停止连接
                    StopConnect();
                    return;
                }
                if (len <= 0)
                {
                    // 服务器端正常退出
                    AppendTextToTxtLog(string.Format("服务器端{0}正常退出", proxSocket.RemoteEndPoint.ToString()));
                    // 停止连接
                    StopConnect();

                    return; // 让方法结束，即结束当前线程
                }


                // 先根据接收到的data字节数组的第一个字节是多少，判断数据类型是什么，1是字符串，2是闪屏，3是文件
                if (data[0] == 1)
                {
                    dataToString(data); // 将字节数组转换为字符串信息
                }
                else if (data[0] == 2)
                {
                    Shake();
                }
                else if (data[0] == 3)
                {
                    dataToFile(data, len);
                }
                
            }
        }
        #endregion

        private void dataToFile(byte[] data, int len) // 将字节数组转换为文件
        {
            if (InvokeRequired)
            {
                Invoke(new Action(() => dataToFile(data, len)));
                return;
            }

            using (SaveFileDialog sfd = new SaveFileDialog())
            {
                sfd.DefaultExt = "txt";
                sfd.Filter = "文本文件(*.txt)|*.txt|所有文件(*.*)|*.*";
                if(sfd.ShowDialog(this) != DialogResult.OK)
                {
                    return;
                }

                byte[] fileData = new byte[len - 1];
                Buffer.BlockCopy(data, 1, fileData, 0, fileData.Length);
                File.WriteAllBytes(sfd.FileName, fileData);
            }
        }

        private void Shake() // 闪屏
        {   
            Point oldLocation = this.Location; // 窗体当前位置
            Random random = new Random();
            
            for(int i = 0; i < 20; i++)
            {
                
                if (this.InvokeRequired)
                {
                    this.Invoke(new Action<Form>(s =>
                    {
                        this.Location = new Point(random.Next(oldLocation.X - 5, oldLocation.X + 5), random.Next(oldLocation.Y - 5, oldLocation.Y + 5));
                        Thread.Sleep(50);
                        this.Location = oldLocation;
                    }), this);
                }
                else
                {
                    this.Location = new Point(random.Next(oldLocation.X - 5, oldLocation.X + 5), random.Next(oldLocation.Y - 5, oldLocation.Y + 5));
                    Thread.Sleep(50);
                    this.Location = oldLocation;
                }
            }
        }
        

        private void dataToString(byte[] data) // 将字节数组转换为字符串信息
        {
            string txt = Encoding.Default.GetString(data, 1, data.Length - 1);
            AppendTextToTxtLog(string.Format("接收到服务器端的消息是：{0}", txt));
        }
        

        private void StopConnect() // 停止连接
        {
            try
            {
                if (ClientSocket.Connected)
                {
                    ClientSocket.Shutdown(SocketShutdown.Both);
                    ClientSocket.Close(100);
                }
            }
            catch (Exception ex)
            {

            }
        }

        private void btn_SendMessage_Click(object sender, EventArgs e)
        {
            if(ClientSocket.Connected)
            {
                byte[] data = Encoding.Default.GetBytes(txt_Message.Text);
                ClientSocket.Send(data, 0, data.Length, SocketFlags.None);
            }
        }

        // 往聊天框上追加数据
        public void AppendTextToTxtLog(string txt)
        {
            // 判断当前线程是否是主线程（UI线程）避免跨线程操作
            if (txt_Log.InvokeRequired)
            {
                txt_Log.Invoke(new Action<string>(s =>
                {
                    this.txt_Log.Text = string.Format("{0}\r\n{1}", s, txt_Log.Text);
                }), txt);
            }
            else
            {
                this.txt_Log.Text = string.Format("{0}\r\n{1}", txt, txt_Log.Text);
            }
        }

        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            // 判断是否已连接服务器端，若已连接，则关闭连接
            StopConnect();
        }
    }
}
